home *** CD-ROM | disk | FTP | other *** search
/ The 640 MEG Shareware Studio 2 / The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO / clang / dbread2.zip / DBREAD2.C < prev    next >
Text File  |  1992-01-08  |  16KB  |  497 lines

  1. /* Opens a DBF file and gives some information about the structure */
  2.  
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <fcntl.h>
  7. #include <ctype.h>
  8. #include <dos.h>
  9. #include <errno.h>
  10. #include <sys\types.h>
  11. #include <sys\stat.h>
  12. #include <io.h>
  13. #pragma pack(1)
  14.  
  15. /* Some dBASE constants */
  16. #define VERSION "1.1c"
  17. #define MAXFIELDS 255
  18. #define DB3FDASIZE 32
  19. #define DB2FDASIZE 16
  20. #define DBF_VERSION_MASK 7
  21. #define DB4_MEMO_MASK 8
  22. #define ANY_MEMO_MASK 128
  23. #define FINDFIRST _dos_findfirst
  24. #define FINDNEXT _dos_findnext
  25. #define SPLITPATH _splitpath
  26. #define FULLPATH _fullpath
  27.  
  28. /* Define dBASE II header structure without including field
  29.    descriptor array, from "dBASE Power:.." book */
  30. typedef struct
  31. {
  32.    char first_byte;
  33.    int num_recs;
  34.    char last_upd_month;
  35.    char last_upd_day;
  36.    char last_upd_yr;
  37.    int rec_bytes;
  38. } DB2_HEADER;
  39.  
  40. /* Define dBASE II field descriptor array, from "dBASE Power:..." */
  41. typedef struct
  42. {
  43.    char field_name[11];
  44.    char field_type;
  45.    char field_length;
  46.    int dloc;
  47.    char field_decimal;
  48. } DB2_FDA;
  49.  
  50. /* Define dBASE III,IV header structure without including field
  51.    descriptor array, see page E-2 of the dBASE IV Language Reference */
  52. typedef struct
  53. {
  54.    unsigned char first_byte;
  55.    unsigned char last_upd_yr;
  56.    unsigned char last_upd_month;
  57.    unsigned char last_upd_day;
  58.    unsigned long num_recs;
  59.    unsigned int head_bytes;
  60.    unsigned int rec_bytes;
  61.    unsigned char reserved_1[2];
  62.    unsigned char incomplete;
  63.    unsigned char is_encrypted;
  64.    unsigned char reserved_for_lan[12];
  65.    unsigned char has_mdx;
  66.    unsigned char reserved_2[3];
  67. } DB3_HEADER ;
  68.  
  69. /* Define structure for dBASE III,IV array of field descriptors,
  70.    see page E-3 of the dBASE IV Language Reference */
  71. typedef struct {
  72.    unsigned char field_name[11];
  73.    unsigned char field_type; /* C D F L M N */
  74.    unsigned char reserved1[4];
  75.    unsigned char field_length; /* in binary */
  76.    unsigned char field_decimal; /* in binary */
  77.    unsigned char reserved2[2];
  78.    unsigned char work_area_id;
  79.    unsigned char reserved3[10];
  80.    unsigned char has_tag; /* 01H if has tag in production MDX, 00H if not */
  81. } DB3_FDA;
  82.  
  83. union DBF_HEADER
  84. {
  85.    DB2_HEADER db2;
  86.    DB3_HEADER db3;
  87. } in_header;
  88.  
  89. union DBF_FDA
  90. {
  91.    DB2_FDA db2;
  92.    DB3_FDA db3;
  93. } fd_array[ MAXFIELDS + 1 ];
  94.  
  95. void FullName( char *fileName, char *fileExt );
  96. void db3_stru_read( void );
  97. void db3_stru_list( void );
  98. void db2_stru_read( void );
  99. void db2_stru_list( void );
  100. void arg_to_valid_path( char *arg, char *destination, char *default_ext );
  101. int valid_db2( DB2_HEADER *header );
  102. int valid_db3( DB3_HEADER *header );
  103.  
  104.  
  105. /* Structure for file information.  Used by _dos_findfirst, _dos_findnext */
  106. struct find_t file_info;
  107. /*  struct find_t
  108.   {                        // File information return structure
  109.       char reserved [21];  // Reserved for use by DOS
  110.       char attrib;         // Attribute byte for matched path
  111.       unsigned wr_time;    // Time of last write to file
  112.       unsigned wr_date;    // Date of last write to file
  113.       long size;           // Length of file in bytes
  114.       char name [13];      // Null-terminated name of matched
  115.                            //   file/directory, without the path
  116.   };
  117. */
  118.  
  119. /* String for input file name */
  120. char input_file[_MAX_PATH];
  121. char drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT];
  122. unsigned char dbf_version;
  123. int input_handle, field_counter, dbf_count = 0;
  124.  
  125. void main( int argc, char *argv[] )
  126. {
  127.    /* Make sure there was at least one command line argument besides
  128.       program name */
  129.    if( argc < 2 )
  130.    {
  131.       printf( "\nDBRead version %s by Martin R. León\n\n"\
  132.          "Missing command line argument.\n"\
  133.          "USAGE:\tDBREAD <DBF file name>\n\n"\
  134.          "File name may include a drive and wildcard specifications.\n",
  135.          VERSION );
  136.       return;
  137.    }
  138.  
  139.    arg_to_valid_path( argv[1], input_file, ".DBF" );
  140.  
  141.    /* Now that we have all the path components in input_file, split it out
  142.       to individual components again */
  143.    SPLITPATH( input_file, drive, dir, fname, ext );
  144.  
  145.    /* Check for a file specification that matches the one specified on
  146.       command line */
  147.    if( FINDFIRST( input_file, _A_RDONLY | _A_SYSTEM | _A_HIDDEN, &file_info ))
  148.    {
  149.       /* If none found, try adding DBF extension */
  150.       FullName( input_file, "DBF" );
  151.  
  152.       if( FINDFIRST( input_file, _A_RDONLY | _A_SYSTEM | _A_HIDDEN, &file_info ))
  153.       {
  154.          /* Didn't find any matching files, show error and abandon */
  155.          printf( "\nNo files found matching %s\n", input_file );
  156.          return;
  157.       }
  158.    }
  159.  
  160.    printf( "\nDBRead version %s by Martin R. León\n", VERSION );
  161.  
  162.    /* Show structure information for all files that meet the file
  163.       specification indicated on command line */
  164.    do
  165.    {
  166.       /* Create new input file name from original drive and directory
  167.          and add file name resulting from the most recent FINDFIRST or
  168.          FINDNEXT calls */
  169.       strcpy( input_file, drive );
  170.       strcat( input_file, dir );
  171.       strcat( input_file, file_info.name );
  172.  
  173.       /* Open file for read-only in binary mode and make sure there are
  174.          no errors openning it */
  175.       if((input_handle = open(input_file, O_RDONLY | O_BINARY, S_IREAD)) == -1)
  176.       {
  177.          printf( "\nUnable to open file %s\n", input_file );
  178.          continue;
  179.       }
  180.  
  181.       /* Read record header to in_header and check for error */
  182.       if( read( input_handle, &in_header, sizeof(in_header) ) == -1 )
  183.       {
  184.          printf( "\nError reading header from file!\n" );
  185.          if( close( input_handle ) != 0 )
  186.             printf( "\nError closing file!\n" );
  187.          else
  188.             printf( "\nFile closed!\n" );
  189.          return;
  190.       }
  191.  
  192.       /* Move file pointer back to beginning */
  193.       lseek( input_handle, 0, SEEK_SET );
  194.  
  195.       /* Try to validate as a DB2 DBF file */
  196.       if( valid_db2( &in_header.db2 ) )
  197.       {
  198.          /* This is probably a good DB2 file, read the structure & list it */
  199.          db2_stru_read();
  200.          db2_stru_list();
  201.          dbf_count++;
  202.       }
  203.       else
  204.          /* Try to validate as a dB III/IV DBF file */
  205.          if( valid_db3( &in_header.db3 ) )
  206.          {
  207.             /* This is probably a good DB III/IV file, read structure & list it */
  208.             db3_stru_read();
  209.             db3_stru_list();
  210.             dbf_count++;
  211.          }
  212.       else
  213.          /* If this file has an extension that should contain a valid DBF but
  214.             the header is unreadable, display message. */
  215.          if(strstr( input_file, ".DBF" ) || strstr( input_file, ".DBK" ) ||
  216.             strstr( input_file, ".DBB" ) || strstr( input_file, ".CRP" ) ||
  217.             strstr( input_file, ".CVT" ) || strstr( input_file, ".CAT" ) ||
  218.             !(strstr( fname, "*") || strstr( fname, "?" )) ||
  219.             !(strstr( ext, "*") || strstr( ext, "?" )) )
  220.  
  221.             printf( "\n%s is not a valid DBF file.\n", input_file );
  222.  
  223.       /* Close file */
  224.       if( close( input_handle ) != 0 )
  225.          printf( "\nError closing file!\n" );
  226.  
  227.    /* Go until no more files meet file specification from command line */
  228.    }  while( !FINDNEXT( &file_info ) );
  229.  
  230.    if( !dbf_count )
  231.       printf( "\nNo valid DBF structures found in %s%s%s%s\n",
  232.          drive, dir, fname, ext );
  233.  
  234.    exit( 0 );
  235. }
  236.  
  237. void db3_stru_read( void )
  238. {
  239.    /* Read rest of header */
  240.    read( input_handle, &in_header.db3, (sizeof( in_header.db3 )) );
  241.  
  242.    /* Read in field descriptor array.  Read first field descriptor array
  243.       then go into for loop until 0DH is read into the field name. */
  244.    read( input_handle, &fd_array[1].db3, DB3FDASIZE );
  245.    for( field_counter = 1; field_counter <= MAXFIELDS; field_counter++ )
  246.    {
  247.       if( fd_array[ field_counter ].db3.field_name[ 0 ] == 0x0D )
  248.       {
  249.          field_counter--;
  250.          break;
  251.       }
  252.       read( input_handle, &fd_array[field_counter + 1].db3, DB3FDASIZE );
  253.    }
  254. }
  255.  
  256. void db3_stru_list( void )
  257. {
  258.    int x;
  259.  
  260.    /* Print out header information */
  261.    printf( "\nDatabase name: %s\n"\
  262.       "DBF version: %d                 Any Memo's ?  %c\t\n"\
  263.       "dBASE IV Memo ?  %c             Last Update:  %d/%d/%d\n"\
  264.       "Number of records: %10ld  Bytes per record: %u\n"\
  265.       "Encrypted ?  %c                 MDX ?  %c\n",
  266.       input_file,
  267.       in_header.db3.first_byte & DBF_VERSION_MASK,
  268.       ((in_header.db3.first_byte & ANY_MEMO_MASK) ? 'Y' : 'N' ),
  269.       ((in_header.db3.first_byte & DB4_MEMO_MASK) ? 'Y' : 'N' ),
  270.       in_header.db3.last_upd_month,
  271.       in_header.db3.last_upd_day,
  272.       in_header.db3.last_upd_yr,
  273.       in_header.db3.num_recs,
  274.       in_header.db3.rec_bytes,
  275.       (in_header.db3.is_encrypted) ? 'Y' : 'N',
  276.       (in_header.db3.has_mdx) ? 'Y' : 'N' );
  277.  
  278.    /* If the file is encrypted, we can't read the structure, so don't try */
  279.    if( in_header.db3.is_encrypted )
  280.       return;
  281.  
  282.    /* Print out field information */
  283.    if( in_header.db3.has_mdx )
  284.       printf( "\nField name\tType\tLen\tDec\tMDX\n\n" );
  285.    else
  286.       printf( "\nField name\tType\tLen\tDec\n\n" );
  287.  
  288.    for( x = 1; x <= field_counter; x++ )
  289.    {
  290.       printf( "%-10s\t%c\t%d\t",
  291.          fd_array[ x ].db3.field_name,
  292.          fd_array[ x ].db3.field_type,
  293.          fd_array[ x ].db3.field_length );
  294.  
  295.       if( fd_array[ x ].db3.field_type == 'N' )
  296.          printf( "%d\t", fd_array[ x ].db3.field_decimal );
  297.       else
  298.          printf( "\t" );
  299.  
  300.       if( in_header.db3.has_mdx )
  301.          printf( "%c\n", ((fd_array[ x ].db3.has_tag) ? 'Y' : 'N') );
  302.       else
  303.          printf( "\n" );
  304.    }
  305.    return;
  306. }
  307.  
  308. void db2_stru_read( void )
  309. {
  310.    /* Read rest of header */
  311.    read( input_handle, &in_header.db2, (sizeof( in_header.db2 )) );
  312.  
  313.    /* Read in field descriptor array.  Read first field descriptor array
  314.       then go into for loop until 0DH is read into the field name. */
  315.    read( input_handle, &fd_array[1].db2, DB2FDASIZE );
  316.    for( field_counter = 1; field_counter <= MAXFIELDS; field_counter++ )
  317.    {
  318.       if( fd_array[ field_counter ].db2.field_name[ 0 ] == 0x0D )
  319.       {
  320.          field_counter--;
  321.          break;
  322.       }
  323.       read( input_handle, &fd_array[field_counter + 1].db2, DB2FDASIZE );
  324.    }
  325. }
  326.  
  327. void db2_stru_list( void )
  328. {
  329.    int x;
  330.  
  331.    /* Print out header information */
  332.    printf( "\nDatabase name: %s\n"\
  333.       "DBF version: %d                 Last Update:  %d/%d/%d\n"\
  334.       "Number of records: %10d  Bytes per record: %i\n",
  335.       input_file,
  336.       in_header.db2.first_byte & DBF_VERSION_MASK,
  337.       in_header.db2.last_upd_month,
  338.       in_header.db2.last_upd_day,
  339.       in_header.db2.last_upd_yr,
  340.       in_header.db2.num_recs,
  341.       in_header.db2.rec_bytes );
  342.  
  343.    printf( "\nField name\tType\tLen\tDec\n\n" );
  344.  
  345.    for( x = 1; x <= field_counter; x++ )
  346.    {
  347.       printf( "%-10s\t%c\t%d\t",
  348.          fd_array[ x ].db2.field_name,
  349.          fd_array[ x ].db2.field_type,
  350.          fd_array[ x ].db2.field_length );
  351.  
  352.       if( fd_array[ x ].db2.field_type == 'N' )
  353.          printf( "%d\n", fd_array[ x ].db2.field_decimal );
  354.       else
  355.          printf( "\n" );
  356.  
  357.    }
  358.    return;
  359. }
  360.  
  361. int valid_db2( DB2_HEADER *header )
  362. {
  363.  
  364. return ( ((header->first_byte & DBF_VERSION_MASK) == 2) &&
  365.    (header->num_recs >= 0 && header->num_recs <= 65534) &&
  366.    (header->rec_bytes >=2 && header->rec_bytes <= 1000) &&
  367.    header->last_upd_month > 0 && header->last_upd_month <= 12 &&
  368.    header->last_upd_day > 0 && header->last_upd_day <=31 &&
  369.    header->last_upd_yr > 0);
  370. }
  371.  
  372. int valid_db3( DB3_HEADER *header )
  373. {
  374.  
  375. return (((header->first_byte & DBF_VERSION_MASK) == 3) &&
  376.    (header->rec_bytes >= 2 && header->rec_bytes <= 4000) &&
  377.    (header->head_bytes >= 63 && header->head_bytes <= 8193 ) &&
  378.    (header->num_recs >= 0 && header->num_recs < 100000000) );
  379.  
  380. }
  381.  
  382.  
  383. /* Adds an extension to a string containing a file name if the specified
  384.    extension isn't already in the string.  String must be larger than the
  385.    combination of the existing string and the extension to be added.
  386.    All done through pointer to original string. */
  387.  
  388. void FullName( char *fileName, char *fileExt )
  389. {
  390.    /* Set ptr to last character before terminator in fiel name */
  391.    char *ptr = fileName + ( strlen( fileName ) - 1 );
  392.  
  393.    /* Check for extension already in name */
  394.    if( strstr( fileName, fileExt ) != NULL )
  395.       return;
  396.  
  397.    /* Extension wasn't found, back up ptr to first non-alpha character,
  398.       stop if we got to beginning of string */
  399.    while( isalpha( (int)*ptr ) )
  400.    {
  401.       if( --ptr == fileName )
  402.          break;
  403.    }
  404.  
  405.    /* If we backed up to a slash, add ".DBF" to the end of the file name
  406.       and return */
  407.    if( *ptr == '\\' )
  408.    {
  409.       fileName = strcat( fileName, ".DBF" );
  410.       return;
  411.    }
  412.  
  413.    /* If we backed up to a period, asterisk or ?, return without adding
  414.       extension */
  415.    if( *ptr == '.' || *ptr == '*' || *ptr == '?' )
  416.       return;
  417.  
  418.    /* Add extension, since it wasn't found */
  419.    strcat( fileName, ".DBF" );
  420.    return;
  421. }
  422.  
  423. /* A function to take a filespec including wildcards, . and .. to produce
  424.    a full proper absolute file path, adding a specified default extension is
  425.    no extension was specified.  Assumes drive, dir, fname, ext are
  426.    global public strings as defined for _splitpath().  destination
  427.    string should be _MAX_PATH long.  Resulting file path is stored in the string
  428.    pointed to by destination.  drive, dir, fname, and ext receive the
  429.    appropriate components of the resulting file path.*/
  430.  
  431. void arg_to_valid_path( char *arg, char *destination, char *default_ext )
  432. {
  433.    char temp_path[ _MAX_PATH ];
  434.  
  435.    *destination = '\0';
  436.  
  437.    /* Create a full file path specification */
  438.    strcpy( destination, strupr(arg) );
  439.    if( strcmp( destination + (strlen( destination ) - 2), ".." ) == 0 )
  440.    {
  441.       strcat( destination, "\\*" );
  442.       strcat( destination, default_ext );
  443.    }
  444.    temp_path[0] = '\0';
  445.    /* Break command line argument into path components.  This first time
  446.       may not yield a file name or extension and the dir component may not
  447.       yield a complete absolute directory reference. */
  448.    SPLITPATH( destination, drive, dir, fname, ext );
  449.    /* Add drive to directory so that we can get a complete absolute directory
  450.       name */
  451.    strcpy( destination, drive );
  452.    strcat( destination, dir );
  453.    strcpy( dir, destination );
  454.    /* Expand drive/path specification to absolute directory name */
  455.    FULLPATH( temp_path, dir, _MAX_PATH );
  456.    /* If just the drive letter was specified, default to default extension */
  457.    if( strlen( drive ) && !strlen( fname ) && !strlen( ext ) )
  458.    {
  459.       strcpy( fname, "*" );
  460.       strcat( ext, default_ext );
  461.    }
  462.    /* If a lone period was specified, convert to default extension */
  463.    if( !strlen( fname ) && strcmp( ext, "." ) == 0 )
  464.    {
  465.       strcpy( fname, "*" );
  466.       strcat( ext, default_ext + 1 );
  467.    }
  468.    /* If a lone asterisk was specified, do the same as above.  This is
  469.       slightly different because extension has no period */
  470.    if( strcmp( fname, "*" ) == 0 && !strlen( ext ) )
  471.    {
  472.       strcpy( fname, "*" );
  473.       strcat( ext, default_ext );
  474.    }
  475.    /* If a file name was specified but no extension, assume default extension */
  476.    if( strlen( fname ) && strcmp( fname, "*" ) != 0 && !strlen( ext ) )
  477.       strcat( ext, default_ext );
  478.    /* If there is a file name but no extension, use default extension */
  479.    if( !strlen( ext ) )
  480.       strcat( ext, default_ext );
  481.  
  482.    /* Construct absolute input file name from path components */
  483.    strcpy( destination, strupr( temp_path ));
  484.    if( destination[ strlen( destination ) - 1 ] != '\\' )
  485.       strcat( destination, "\\" );
  486.    if( strlen( fname ) == 0 )
  487.       strcat( destination, "*" );
  488.    else
  489.       strcat( destination, fname );
  490.    if( strlen( ext ) == 0 )
  491.       strcat( ext, default_ext );
  492.    else
  493.       strcat( destination, ext );
  494.  
  495. }
  496.  
  497.